package cn.funeralobjects.project.aliyun.ddns.service.impl;

import cn.funeralobjects.project.aliyun.ddns.model.DomainRecord;
import cn.funeralobjects.project.aliyun.ddns.service.DdnsService;
import cn.funeralobjects.project.aliyun.ddns.utils.DdnsUtils;
import cn.funeralobjects.util.Assert;
import cn.funeralobjects.utils.NetworkUtils;
import cn.funeralobjects.utils.aliyun.dns.model.DescribeSubDomainRecordsData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author FuneralObjects
 * Create date: 2020/11/29 8:19 PM
 */
@Service
public class DdnsServiceImpl implements DdnsService {

    private static final Logger LOGGER = LoggerFactory.getLogger("DDNS-SERVICE");

    private AtomicReference<String> lastIp;

    private Set<String> cacheDomains;

    @PostConstruct
    private void init() {
        lastIp = new AtomicReference<>("");
        cacheDomains = ConcurrentHashMap.newKeySet();
    }

    private static String getIp() {
        return NetworkUtils.getInternetIpv4(LOGGER);

    }

    @Override
    public boolean freshIp() {
        return Optional.of(getIp())
                .filter(ip -> !ip.equals(lastIp.get()))
                .map(ip -> {
                    LOGGER.info("monitor the internet ip changed. before: {}, now: {}", lastIp.get(), ip);
                    lastIp.set(ip);
                    return lastIp.get();
                }).isPresent();
    }

    @Override
    public String getCurrentIp() {
        return lastIp.get();
    }

    @Override
    public void update(String recordFile, String ignorePrefix) {
        Assert.argHasText("recordFile", recordFile);
        Assert.argHasText("ignorePrefix", ignorePrefix);

        List<DomainRecord> domainRecords = DdnsUtils.loadRecords(recordFile, ignorePrefix, LOGGER);

        boolean isFresh = freshIp();

        domainRecords.stream()
                .filter(domainRecord -> {
                    String domain = domainRecord.getSubDomain() + "." + domainRecord.getDomain();
                    boolean isNotSkip = isFresh || !cacheDomains.contains(domain);
                    if (!isNotSkip) {
                        LOGGER.info("The record {} are be skipped.", domain);
                    }
                    return isNotSkip;
                })
                .map(record -> Map.entry(record, Objects.requireNonNull(DdnsUtils.loadDescribeRecords(record, LOGGER))))
                .filter(entry -> DdnsUtils.isCanBeUpdate(lastIp.get(), entry.getValue(), LOGGER))
                .forEach(this::updateIp);

    }

    private void updateIp(Map.Entry<DomainRecord, DescribeSubDomainRecordsData> recordsData) {
        if (recordsData.getValue().getTotalCount() >= 1) {
            //update
            DdnsUtils.updateDescribeRecord(lastIp.get(), recordsData.getKey(), recordsData.getValue(), LOGGER);
        } else {

            DdnsUtils.addDescribeRecord(lastIp.get(), recordsData.getKey(), LOGGER);
        }
        //add new
        this.cacheDomains.add(recordsData.getKey().getSubDomain() + "." + recordsData.getKey().getDomain());
    }


}
